package gov.nasa.anml.lifted;

import java.util.ArrayList;
import java.util.List;

import gov.nasa.anml.PDDL;
import gov.nasa.anml.PDDL.Predicate;
import gov.nasa.anml.utility.*;

public abstract class Unit<Value,Storage extends SimpleObject<? super Storage>>  
	extends	ScopedIdentifierImp<Value, Storage> implements LabeledInterval<Value,Storage> {

	public static ObjectType objectType;
	public static SymbolType symbolType;
	
	public static PrimitiveType<SimpleBoolean> booleanType;
	public static PrimitiveType<SimpleInteger> integerType;
	public static PrimitiveType<SimpleString> stringType;
	public static PrimitiveType<SimpleFloat> floatType;
	
	public static PrimitiveType<SimpleCharacter> characterType;

	// All, or as much as reasonable, (global) initialization should happen here, so that the order can be guaranteed, since
	// the ClassLoader doesn't provide good guarantees.  In particular the types need to exist before most things.
	static {
		// primitive types must be declared before structured types, because structured types
		// have time scopes, which are represented as constants with references to the primitive types
		// (so these have to exist first)
		booleanType = new PrimitiveType<SimpleBoolean>(TypeCode.Boolean);
		integerType = new PrimitiveType<SimpleInteger>(TypeCode.Integer);
		stringType = new PrimitiveType<SimpleString>(TypeCode.String);
		floatType = new PrimitiveType<SimpleFloat>(TypeCode.Float);
		characterType = new PrimitiveType<SimpleCharacter>(TypeCode.Character);
		objectType = new ObjectType(null,TypeCode.Object.name);
		symbolType = new SymbolType(TypeCode.Symbol.name);
		
//		
//		char[] bName = {'b','o','o','l','e','a','n'};
//		booleanType.name = new SimpleString(bName);
//		char[] iName = {'i','n','t','e','g','e','r'};
//		integerType.name = new SimpleString(iName);
//		char[] sName = {'s','t','r','i','n','g'};
//		stringType.name = new SimpleString(sName);
//		char[] fName = {'f','l','o','a','t'};
//		floatType.name = new SimpleString(fName);
//		char[] chName = {'c','h','a','r','a','c','t','e','r'};
//		characterType.name = new SimpleString(chName);
//		char[] oName = {'o','b','j','e','c','t'};
//		objectType.name = new SimpleString(oName);
//		char[] syName = {'s','y','m','b','o','l'};
//		symbolType.name = new SimpleString(syName);

		// array literals are (or rumored to be) quite inefficient, unlike String
		// literals, which is bizarre.

		// the many levels of nesting are useful for start/duration/end,
		// but less useful for the delimiters. Still, we can imagine a way of
		// referring to a containing interval's shape, and this would have some kind
		// of name. Whatever that name is the correct initialization strings below
		// --
		// these would be inserted into a symbol table so that the parser doesn't
		// even realize what's going on.
		IntervalImp.constantBeforeBra = new Constant<SimpleInteger>((SimpleInteger)ANMLInteger.make(Time.Before), new SimpleString("..."), Unit.integerType);
		IntervalImp.constantAtBra = new Constant<SimpleInteger>((SimpleInteger)ANMLInteger.make(Time.At), new SimpleString("["), Unit.integerType);
		IntervalImp.constantAfterBra = new Constant<SimpleInteger>((SimpleInteger)ANMLInteger.make(Time.After), new SimpleString("("), Unit.integerType);
		IntervalImp.constantBeforeKet = new Constant<SimpleInteger>((SimpleInteger)ANMLInteger.make(Time.Before), new SimpleString(")"), Unit.integerType);
		IntervalImp.constantAtKet = new Constant<SimpleInteger>((SimpleInteger)ANMLInteger.make(Time.At), new SimpleString("]"), Unit.integerType);
		IntervalImp.constantAfterKet = new Constant<SimpleInteger>((SimpleInteger)ANMLInteger.make(Time.After), new SimpleString("..."), Unit.integerType);
		// do not use these without a lot more thought.
		IntervalImp.constantDurationZero = new Constant<SimpleFloat>((SimpleFloat)ANMLFloat.ZeroF, Interval.durationName, Unit.floatType);
		IntervalImp.constantDurationOne = new Constant<SimpleFloat>((SimpleFloat)ANMLFloat.OneF,Interval.durationName,Unit.floatType);
	}
	
	public Constant<SimpleFloat> start;
	public Constant<SimpleFloat> duration;
	public Constant<SimpleFloat> end;
	public Constant<SimpleInteger> bra;
	public Constant<SimpleInteger> ket;

	
	public Unit() {}
	public Unit(Scope parent) {	super(parent);_Unit_init();}
	public Unit(Scope parent, SimpleString n) {super(parent,n);_Unit_init();}
	public Unit(Scope parent, SimpleString n,Constant<SimpleFloat> dur) {init(parent,n,dur);}
	public Unit(Scope parent, SimpleString n,Constant<SimpleFloat> s, Constant<SimpleFloat> dur) {init(parent,n,s,dur);}
	public Unit(Scope parent, SimpleString n,Constant<SimpleFloat> s,Constant<SimpleFloat> dur, Constant<SimpleFloat> e) {init(parent,n,Time.At,s,dur,e,Time.At);}

	protected final void _Unit_init() {
		start = new Constant<SimpleFloat>(IntervalImp.startName,Unit.floatType);
		duration = new Constant<SimpleFloat>(IntervalImp.durationName,Unit.floatType);
		end = new Constant<SimpleFloat>(IntervalImp.endName,Unit.floatType);
		bra = IntervalImp.constantAtBra;
		ket = IntervalImp.constantAtKet;
	}
	public final void init(Scope parent, SimpleString n, Constant<SimpleFloat> dur) {
		start = new Constant<SimpleFloat>(IntervalImp.startName,Unit.floatType);
		duration = dur;
		end = new Constant<SimpleFloat>(IntervalImp.endName,Unit.floatType);
		bra = IntervalImp.constantAtBra;
		ket = IntervalImp.constantAtKet;
	}
	public final void init(Scope parent, SimpleString n, Constant<SimpleFloat> s, Constant<SimpleFloat> dur) {
		start = s;
		duration = dur;
		end = new Constant<SimpleFloat>(IntervalImp.endName,Unit.floatType,new OpBinary<SimpleFloat,SimpleFloat,SimpleVoid>(TypeCode.Float,Op.add,s,dur));
		bra = IntervalImp.constantAtBra;
		ket = IntervalImp.constantAtKet;
	}
	public final void init(int b, Constant<SimpleFloat> s, Constant<SimpleFloat> dur, int k) {
		start = s;
		duration = dur;
		end = new Constant<SimpleFloat>(IntervalImp.endName,Unit.floatType,new OpBinary<SimpleFloat,SimpleFloat,SimpleVoid>(TypeCode.Float,Op.add,s,dur));
		bra = IntervalImp.makeBra(b);
		ket = IntervalImp.makeKet(k);
	}
	// end - start - duration == 0 had better hold...
	public final void init(Scope parent, SimpleString n, int b, Constant<SimpleFloat> s, Constant<SimpleFloat> dur, Constant<SimpleFloat> e, int k) {
		start = s;
		duration = dur;
		end = e; 
		bra = IntervalImp.makeBra(b);
		ket = IntervalImp.makeKet(k);
	}
	
	
	public Constant<SimpleFloat> getStart() {
		return start;
	}

	public Constant<SimpleFloat> getDuration() {
		return duration;
	}

	public Constant<SimpleFloat> getEnd() {
		return end;
	}

	public Constant<SimpleInteger> getBra() {
		return bra;
	}

	public Constant<SimpleInteger> getKet() {
		return ket;
	}

	public void setStart(Constant<SimpleFloat> start) {
		this.start = start;
	}

	public void setDuration(Constant<SimpleFloat> duration) {
		this.duration = duration;
	}

	public void setEnd(Constant<SimpleFloat> end) {
		this.end = end;
	}

	// setShape, setBra, and setKet are copied from Interval.  One could make Unit inherit from Interval instead, but, 
	// more mileage is obtained by inheriting from the SymbolTable/Scope hierarchy.  
	// This is a good example of where multiple-inheritance, prohibited by Java, makes sense.
	public void setShape(int b, int k) {
		setBra(b);
		setKet(k);
	}
	
	public void setBra(int b) {
		switch(b) {
			case Before:
				bra = IntervalImp.constantBeforeBra;
				// Not a fantastic concept.  For points, ...t] is the notation.
				// for intervals, ...s,t] is I think right.
				break;
			case At:
				bra = IntervalImp.constantAtBra;
				break;
			case After:
				bra = IntervalImp.constantAfterBra;
				break;
			default:
				System.err.println("Invalid Time Code");
		}
	}
	
	public void setKet(int k) {
		switch(k) {
			case Before:
				ket = IntervalImp.constantBeforeKet;
				break;
			case At:
				ket = IntervalImp.constantAtKet;
				break;
			case After:
				ket = IntervalImp.constantAfterKet;  
				// Not a fantastic concept, but I guess the notation is [t... for points
				// for intervals, [s,t... might be acceptable?  Probably.
				break;
			default:
				System.err.println("Invalid Time Code");
		}
	}

	
	
	
	
	
	
	
	
	
	
	public transient PDDL.Action asPDDLAction;
	
	public ArrayList<PDDL.BooleanExpression> getPDDLConditions() {
		return asPDDLAction.condition.arguments;
	}
	public ArrayList<PDDL.Effect> getPDDLEffects() {
		return asPDDLAction.effect.arguments;
	}
	public PDDL.FloatExpression getPDDLDuration() {
		return asPDDLAction.duration;
	}
	public ArrayList<PDDL.Parameter> getPDDLParameters() {
		return asPDDLAction.parameters;
	}
	
	public Predicate getPDDLExecuting() {
		return asPDDLAction.executing;
	}

	public PDDL.Action getPDDLAction() {
		return this.asPDDLAction;
	}
	
	public Predicate makePDDLExecuting() {
		return asPDDLAction.makeExecuting();
	}
	
	protected void _translateDecl(PDDL pddl,Interval unit) {
		
		for(Type t : this.types.table) {
			t.translateDecl(pddl,unit);
		}
		for (Constant c : this.constants.table) {
			c.translateDecl(pddl,unit);
		}
		for (ConstantFunction c : this.constantFunctions.table) {
			c.translateDecl(pddl,unit);
		}
		for (Fluent f : this.fluents.table) {
			f.translateDecl(pddl,unit);
		}
		for (FluentFunction f : this.fluentFunctions.table) {
			f.translateDecl(pddl,unit);
		}

		// labels?
		for (Action a : this.actions.table) {
			a.translateDecl(pddl,unit);
		}		
		for (Statement s : this.statements) {
			s.translateDecl(pddl,unit);
		}		
		for (Decomposition d : this.decompositions) {
			d.translateDecl(pddl,unit);
		}		
		
	}
	
	protected void _translateStmt(PDDL pddl, Interval unit, PDDL.Time time) {
		for (Action a : this.actions.table) {
			a.translateStmt(pddl,unit,time);
		}		
		for (Statement s : this.statements) {
			s.translateStmt(pddl,unit,time);
		}		
		for (Decomposition d : this.decompositions) {
			d.translateStmt(pddl,unit,time);
		}		
	}
	
	// make all blocks have time scopes...?
	// get rid of special-ness of domain...?
	// scopeimp
	// identscopeimp
	//     object types aren't timed (?)
	//     so compulsory and choosable 
	// 'unit' == timedidentscopeimp
	//     labels
	//     compulsory units
	//     choosable units
	//     decomps  [disjunction over compulsory units]
	//     
	// block == anonymous unit  (name == null) [non-special]
	// action == choosable 'unit' [also not particularly special]
	// 'action' is like '[all]', it is a time-spec
	//    an existential, and choosable, spec.

	public void translateDecl(PDDL pddl,Interval unit) {		
		if (asPDDLAction != null)
			return;
		
		int length = pddl.bufAppend(name);
	
		String n = pddl.bufToString();
		
		if (duration.init instanceof Parameter || (duration.init == null && duration.value == null)) {
			asPDDLAction = pddl.new ComplexAction(n);
		} else {
			asPDDLAction = pddl.new Action(n);
			if(duration.init != null) {
				asPDDLAction.duration = (PDDL.FloatExpression) duration.init.translateExpr(pddl,this);
			} else {
				asPDDLAction.duration = pddl.new FloatLiteral(duration.value.v);
			}
		}
	
		for (PDDL.Parameter p : pddl.context) {
			asPDDLAction.context.add(p);
		}

		int s = pddl.context.size();
		
		for (Parameter<? extends SimpleObject<?>> p : parameters.table) {
			p.translateDecl(pddl,this);
			if (p.asPDDLParameter() != null) 
				pddl.context.add(p.asPDDLParameter());
		}
		
		++pddl.depth;
		
		if (unit != null) {
			PDDL.Predicate executing = unit.makePDDLExecuting();
			asPDDLAction.condition.arguments.add(pddl.wrap(PDDL.Time.Interim,executing.trivialRef()));
		}
		//asPDDLAction.condition.arguments.add(pddl.TrueCondition);
		//asPDDLAction.makeExecuting();
		
		_translateDecl(pddl,this);
		//_translateStmt(pddl); //not safe if statements inside depend on later declarations.
		// despite having the structures built here, the corresponding structures have not yet been made
		// within the PDDL object.
		// ....duh.  Stupid bug.
		
		pddl.bufReset(length);
		int l = pddl.context.size() - s;
		while (--l >= 0)
			pddl.context.remove(s);
		
		--pddl.depth;
	}
	
	public void translateStmt(PDDL pddl,Interval unit,PDDL.Time time) {		

		/* setup context for declarations.  Probably not needed. */
		/*
		int length = pddl.bufAppend(name);
		//String n = pddl.bufToString();// this is inside asPDDLAction.name or whatever its called.
		int s = pddl.context.size();
		for (Parameter<? extends SimpleObject<?>> p : parameters.table) {
			pddl.context.add(p.asPDDLParameter());
		}
		++pddl.depth;
		*/
		
		_translateStmt(pddl,this,PDDL.Time.Timeless);
		
		/*
		 *  Back up context (exit scope).  Probably not needed. 
		 */
		/*
		pddl.bufReset(length);
		int l = pddl.context.size() - s;
		while (--l >= 0)
			pddl.context.remove(s);
		
		--pddl.depth;
		*/
	}
	


}